package services.user;

import java.util.List;

import manager.ReservationManager;

import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.springframework.transaction.annotation.Transactional;

import repositories.LoanRepository;
import services.GenericService;
import services.book.BookService;
import services.book.ReservationService;
import systemconfig.SystemConfiguration;
import users.Person;
import books.Book;
import books.BookInfo;
import books.Loan;
import dtos.BookInfoDTO;

/**
 */
public class LoanService extends GenericService {

    private static final long serialVersionUID = 1L;

    private LoanRepository loanRepository;

    private ReservationService reservationService;

    private BookService bookService;

    private ReservationManager reservationManager;

    private PersonService personService;

    public PersonService getPersonService() {
        return this.personService;
    }

    public void setPersonService(final PersonService personService) {
        this.personService = personService;
    }

    public void setLoanRepository(final LoanRepository loanRepository) {
        this.loanRepository = loanRepository;
    }

    public LoanRepository getLoanRepository() {
        return this.loanRepository;
    }

    public void setReservationService(final ReservationService reservationService) {
        this.reservationService = reservationService;
    }

    public ReservationService getReservationService() {
        return this.reservationService;
    }

    public void setBookService(final BookService bookService) {
        this.bookService = bookService;
    }

    public BookService getBookService() {
        return this.bookService;
    }

    @Transactional(readOnly = true)
    public List<Loan> getCurrentLoansOf(final Person aPerson) {
        return this.getLoanRepository().getCurrentLoansOf(aPerson);
    }

    @Transactional(readOnly = true)
    public List<Loan> getAllLoans() {
        return this.getLoanRepository().findAll();
    }

    @Transactional
    public void newLoanFromReservation(final BookInfo aBookInfo, final Person aPerson) {
        Book copy = this.getReservationService().getReservedBook(aBookInfo);
        this.getReservationService().clearReservation(aBookInfo);

        DateTime loanEnd = this.getEndLoanDate();

        Person personToSave = this.getPersonService().getPersonByID(aPerson);

        Loan newLoan = new Loan(loanEnd, personToSave, copy);

        this.getLoanRepository().save(newLoan);
    }

    @Transactional
    public void newCommonLoan(final BookInfo aBookInfo, final Person aPerson) {

        Person personToSave = this.getPersonService().findById(aPerson.getId());
        this.validateLoanFor(aPerson);
        Book copy = this.getBookService().borrowCopy(aBookInfo);

        DateTime loanEnd = this.getEndLoanDate();
        Loan newLoan = new Loan(loanEnd, personToSave, copy);
        this.getLoanRepository().save(newLoan);
    }

    private void validateLoanFor(final Person aPerson) {
        this.getReservationManager().validateReservationOrLoanFor(aPerson);
    }

    private DateTime getEndLoanDate() {
        DateTime nextEnd = new DateTime(DateTimeZone.forID("America/Argentina/Buenos_Aires"))
                .plusDays(SystemConfiguration.getInstance().getMaxDaysInLoaning());// esto
                                                                                   // en
                                                                                   // el
                                                                                   // constructor
                                                                                   // de
                                                                                   // Loan?

        // una forma feliz de saber que no es feriado
        return nextEnd;
    }

    @Transactional
    private void finishLoan(final Loan loan) {
        Loan toSave = this.getLoanRepository().findById(loan.getId());
        toSave.setFinished(true);
    }

    @Transactional
    public void returnLoan(final Loan loan) {
        this.finishLoan(loan);
        this.getBookService().returnCopy(loan.getLoanedBook());
    }

    @Transactional
    public Loan getSpecificLoanOf(final Person aPerson, final String aBookTitle) {
        // MEJORAR EFICIENCIA TRAYENDO SOLO EL NECESARIO
        List<Loan> list = this.getCurrentLoansOf(aPerson);
        Loan ret = null;

        for (Loan it : list) {
            if (it.getBookInfo().getTitle().equals(aBookTitle)) {
                ret = it;
            }
        }
        return ret;
    }

    @Transactional
    public void renewLoan(final Loan aLoan) {
        Loan before = this.loanRepository.findByExample(aLoan).get(0);
        DateTime newEnd = before.getEnd().plusDays(SystemConfiguration.getInstance().getMaxDaysInLoaning());
        before.setEnd(newEnd);
        this.loanRepository.update(before);
    }

    @Transactional
    public void newCommonLoan(final BookInfoDTO book, final Person loaner) {
        BookInfo bookInfo = this.getBookService().findById(book.getId());
        this.newCommonLoan(bookInfo, loaner);
    }

    public void setReservationManager(final ReservationManager reservationManager) {
        this.reservationManager = reservationManager;
    }

    public ReservationManager getReservationManager() {
        return this.reservationManager;
    }

}
